package com.only4play.practice.ordercenter.core.entity;

import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.NumberUtil;
import com.only4play.codegen.processor.creator.IgnoreCreator;
import com.only4play.codegen.processor.updater.IgnoreUpdater;
import com.only4play.codegen.support.annotation.FieldDesc;
import com.only4play.codegen.support.constants.ValidStatus;
import com.only4play.codegen.support.exception.BusinessException;
import com.only4play.codegen.support.model.BaseEntity;
import com.only4play.practice.common.OrderErrorCode;
import com.only4play.practice.common.enums.AccountType;
import com.only4play.practice.common.enums.OrderState;
import com.only4play.practice.common.enums.OrderType;
import com.only4play.practice.common.other.CodeValue;
import com.only4play.practice.ordercenter.api.model.domainmodel.OrderCompleteModel;
import com.only4play.practice.ordercenter.api.model.domainmodel.OrderCreateModel;
import com.only4play.practice.ordercenter.core.events.OrderEvents;
import com.only4play.practice.ordercommon.pay.PayItem;
import lombok.Data;
import org.springframework.context.ApplicationEventPublisher;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;

//@GenCreator(pkgName = "com.only4play.practice.ordercenter.model.creator")
//@GenUpdater(pkgName = "com.only4play.practice.ordercenter.model.updater")
//// findById方法
//@GenVo(pkgName = "com.only4play.practice.ordercenter.model.vo")
//// findByPage方法
//@GenQuery(pkgName = "com.only4play.practice.ordercenter.model.query")
//@GenCreateRequest(pkgName = "com.only4play.practice.ordercenter.model.request")
//@GenUpdateRequest(pkgName = "com.only4play.practice.ordercenter.model.request")
//@GenQueryRequest(pkgName = "com.only4play.practice.ordercenter.model.request")
//@GenResponse(pkgName = "com.only4play.practice.ordercenter.model.response")
//@GenDto(pkgName = "com.only4play.practice.ordercenter.model.dto")
//
//@GenFeign(pkgName = "com.only4play.practice.ordercenter.model.feign")
//
//// 生成serviceImpl
//@GenObjectMapper(pkgName = "com.only4play.practice.ordercenter.entitymapper")
//// 生成serviceImpl
//@GenMapper(pkgName = "com.only4play.practice.ordercenter.core.mapper")
//@GenService(pkgName = "com.only4play.practice.ordercenter.core.service")
//@GenServiceImpl(pkgName = "com.only4play.practice.ordercenter.core.service.impl")
//
//@GenController(pkgName = "com.only4play.practice.ordercenter.core.controller")
/**
 *
 * @author mengdy
 */
@Data
public class OrderBase extends BaseEntity {


    @FieldDesc(name = "唯一流水号")
    private Long flowNo;

    @FieldDesc(name = "订单金额")
    private Long totalAmount;

    @FieldDesc(name = "账号Id")
    private Long accountId;

    @FieldDesc(name = "账号类型")
    private AccountType accountType;

    @FieldDesc(name = "订单类型、订单类型创建不同的状态机")
    private OrderType orderType;

    /**
     * 支付详情可能会包含在支付前传了一些信息，比如优惠券、优惠金额等信息，计算待支付金额时可能会用到
     */
    @FieldDesc(name = "支付详情")
    private List<PayItem> payList;

    @FieldDesc(name = "待支付金额")
    private Long waitPay;

    @FieldDesc(name = "支付时间")
    private Date payTime;

    @FieldDesc(name = "订单状态")
    private OrderState orderState;


    /**
     * 订单领域最核心的功能是收钱，收钱后修改订单状态；
     * 由于要做成通用的系统，那么订单信息可以说是千变万化的，但本质上都是在描述订单的信息：比如说订单渠道、城市、企业编码等；
     * 这些变化的东西就可以放到一个json里面，存一些课程编号，课程名称等信息
     *
     *
     * 系统压力不大的时候可以这里放，压力大千万不要放这里，额外的表或者es都可以，这样查询效果更好
     * 并且MySQL里存json体，查询、统计不好做，毕竟不是结构化数据，所以es是更好的选择
     * 可以说MySQL数据库只做存储，或者根据id查询
     * 其他操作都放到es里，这样查询速度会更快，而且es可以做聚合、统计等操作
     */
    @FieldDesc(name = "订单信息")
    private List<CodeValue> attrs;

    @FieldDesc(name = "是否已开票")
    private ValidStatus invoiceFlag;

    @IgnoreUpdater
    @IgnoreCreator
    private ValidStatus validStatus;

    /**
     * 订单初始化
     * @param createModel
     */
    public void init(OrderCreateModel createModel, OrderBase orderBase, ApplicationEventPublisher publisher) {
        orderBase.setValidStatus(ValidStatus.VALID);
        orderBase.setInvoiceFlag(ValidStatus.INVALID);
        Long total = getTotalAmount();
        //如果有虚拟资产的抵扣
        if (!IterUtil.isEmpty(createModel.getPayList())) {
            orderBase.setPayList(createModel.getPayList());
            BigDecimal hasPay = payList.stream().map(PayItem::getMoney)
                    .reduce(BigDecimal.ZERO, NumberUtil::add);
            if (NumberUtil.isGreater(hasPay, new BigDecimal(total))) {
                throw new BusinessException(OrderErrorCode.PAY_TOO_BIG);
            }else if(NumberUtil.equals(hasPay,new BigDecimal(total))){
                orderBase.setOrderState(OrderState.SUCCESS);
                orderBase.setWaitPay(0L);
            }else {
                orderBase.setOrderState(OrderState.WAITING);
                orderBase.setWaitPay(total - hasPay.longValue());
            }
        } else {
            //没有虚拟资产抵扣
            orderBase.setPayList(Collections.EMPTY_LIST);
            if (NumberUtil.equals(total, BigDecimal.ZERO)) {
                orderBase.setOrderState(OrderState.SUCCESS);
                orderBase.setWaitPay(0L);
            } else {
                orderBase.setWaitPay(total);
                orderBase.setOrderState(OrderState.WAITING);
            }
        }
        publisher.publishEvent(new OrderEvents.OrderCreateEvent(orderBase,createModel));
    }

    /**
     * 订单完成
     * @param completeModel
     */
    public void complete(OrderCompleteModel completeModel, OrderBase orderBase, ApplicationEventPublisher publisher) {
        if(!Objects.equals(OrderState.WAITING,orderBase.getOrderState())){
            throw new BusinessException(OrderErrorCode.ORDER_NOT_WAIT_PAY);
        }
        if(IterUtil.isEmpty(completeModel.getPayItemList())){
            throw new BusinessException(OrderErrorCode.PAY_LIST_IS_NULL);
        }
        BigDecimal hasPay = completeModel.getPayItemList().stream().map(PayItem::getMoney)
                .reduce(BigDecimal.ZERO, NumberUtil::add);
        if(!NumberUtil.equals(hasPay,orderBase.getWaitPay())){
            throw new BusinessException(OrderErrorCode.PAY_AMOUNT_NOT_EQUAL_WAIT_PAY);
        }
        orderBase.setPayTime(completeModel.getPayTime());
        List<PayItem> payItemList = orderBase.getPayList();
        payItemList.addAll(completeModel.getPayItemList());
        orderBase.setPayList(payItemList);
        orderBase.setOrderState(OrderState.SUCCESS);
    }

    /**
     * 取消订单
     */
    public void cancel(OrderBase orderBase){
        if(!Objects.equals(OrderState.WAITING,orderBase.getOrderState())){
            throw new BusinessException(OrderErrorCode.ORDER_NOT_WAIT_PAY);
        }
        orderBase.setOrderState(OrderState.TIME_OUT_ABANDON);
    }

    public void valid() {
        setValidStatus(ValidStatus.VALID);
    }

    public void invalid() {
        setValidStatus(ValidStatus.INVALID);
    }

    // TODO 还有一些像 优惠信息； 商家、企业信息； 支付信息如支付方式、交易类型、支付渠道等；







}
